﻿
package extremefx.collections {
	import extremefx.IEnumerable;
	import extremefx.IEnumerator;
	
	import flash.net.registerClassAlias;
	import flash.utils.IDataInput;
	import flash.utils.IDataOutput;	

	/**
	 * @author Marcelo Volmaro
	 */
	 
	public final class List implements ICollection, IEnumerable{
		registerClassAlias("extremefx.collections.LinkedList", List);
		
		internal var _count:uint;
		internal var _head:LListNode;
		internal var _tail:LListNode;
		
		public function List(...args) {
			_count = 0;
			if (args.length) push.apply(this, args);
		}
		
		public function push(...args):void {
			var l:int = args.length;
			var node:LListNode = new LListNode(args[0]);
			
			if (_head) {
				_tail._next = node;
				node._prev = _tail;
				_tail = node;
				
			} else {
				_head = _tail = node;
			}
			
			_count+= l;
			
			if (l) {
				for (var i:uint = 1; i < l; ++i) {
					node = new LListNode(args[i]);

					_tail._next = node;
					node._prev = _tail;
					_tail = node;
				}
			} 
		}
		
		public function unshift(...args):void {
			var l:int = args.length - 1;
			var node:LListNode = new LListNode(args[l]);
			
			if (_head) {
				_head._prev = node;
				node._next = _head;
				_head = node;
				
			} else {
				_head = _tail = node;
			}
			
			_count += (l + 1);
			
			if (l) {
				while (l--){
					node = new LListNode(args[l]);
					
					_head._prev = node;
					node._next = _head;
					_head = node;
				}
			}
		}
		
		public function shift():* {
			if (_head){
				var val:* = _head._value;
				
				if ((_head = _head._next)){
					_head._prev = null;
					
				} else {
					_tail = null;
				}
				
				_count--;
				return val;
			}
			
			return null;
		}
		
		public function pop():* {
			if (_tail){
				var val:* = _tail._value;
				
				if ((_tail = _tail._prev)){
					_tail._next = null;
					
				} else {
					_head = null;
				}
				
				_count--;
				return val;
			}
			
			return null;
		}
		
		public function contains(pValue:*):Boolean {
			var node:LListNode = _head;
			
			while (node){
				if (node._value === pValue) return true;
				node = node._next;
			}
			
			return false;
		}
		
		public function clear():void {
			var node:LListNode = _head;
			_head = null;
			
			var next:LListNode;
			while (node) {
				next = node._next;
				node._next = node._prev = null;
				node = next;
			}
			
			_count = 0;
		}
		
		public function toArray():Array {
			var a:Array = [], i:uint = 0;
			var node:LListNode = _head;

			while (node) {
				a[i++] = node._value;
				node = node._next;
			}
			
			return a;
		}
		
		public function getEnumerator():IEnumerator {
			return new LLEnum(_head);
		}
		
		public function readExternal(input:IDataInput):void {
			var l:uint = input.readUnsignedInt();
			
			if (l){
				while (l--){
					push(input.readObject());
				}
			}
		}
		
		public function writeExternal(output:IDataOutput):void {
			output.writeUnsignedInt(_count);
			
			var node:LListNode = _head;
			while (node) {
				output.writeObject(node._value);
				node = node._next;
			}
		}
		
		public function get count():uint {
			return _count;
		}
	}
}

import extremefx.IEnumerator;

final class LListNode {
	internal var _prev:LListNode = null;
	internal var _next:LListNode = null;
	internal var _value:*;
	
	public function LListNode(pValue:* = null) {
		_value = pValue;
	}
}

final class LLEnum implements IEnumerator {
	private var _head:LListNode;
	private var _node:LListNode;
	
	public function LLEnum(pHead:LListNode) {
		_head = pHead;
	}

	public function moveNext():Boolean {
		if (_node) {
			_node = _node._next;
			
		} else {
			_node = _head;
		}
		
		return _node != null;
	}
	
	public function reset():void {
		_node = null;
	}
	
	public function get current():* {
		return _node._value;
	}
}